/**
 * Created by JetBrains PhpStorm.
 * User: shaoshuai
 * Date: 11-10-5
 * Time: PM5:06
 * To change this template use File | Settings | File Templates.
 */
(function (window) {
    var Jxt = {
        version: '0.0.1'
    };

    Jxt._xtype = {};
    Jxt._idCache = {};
    Jxt._idCount = 0;

    Jxt.apply = function (o, c) {
        if (o && c && typeof c === 'object') {
            for (var p in c) {
                if (c.hasOwnProperty(p)) {
                    o[p] = c[p];
                }
            }
        }
        return o;
    };

    Jxt.apply(Jxt, {
        extend: function (parent, c) {
            var F = function () {
            };
            F.prototype = parent.prototype;
            var f = new F();
            this.apply(f, c);
            var child;

            if (c.constructor && c.constructor !== Object.prototype.constructor) {
                child = c.constructor;
            } else {
                child = function () {
                    parent.apply(this, arguments);
                };
            }
            child.superclass = parent.prototype;
            child.prototype = f;
            child.prototype.constructor = child;
            return child;
        },
        getId: function () {
            this._idCount++;
            return 'jxt-id-' + this._idCount;
        },
        each: function (array, fn, scope) {
            if (Object.prototype.toString.call(array) === '[object Array]') {
                for (var i = 0; i < array.length; i++) {
                    if (fn.call(scope || array[i], array[i], i) === false) {
                        return i;
                    }
                }
            }
        },
        isDefined: function (v) {
            return typeof v !== 'undefined';
        },
        applyIf: function (o, c) {
            if (o) {
                for (var p in c) {
                    if (c.hasOwnProperty(p) && !Jxt.isDefined(o[p])) {
                        o[p] = c[p];
                    }
                }
            }
            return o;
        }
    });

    Jxt.applyIf(Array.prototype, {
        indexOf: function (searchElement, fromIndex) {
            var len = this.length;
            fromIndex = fromIndex || 0;
            fromIndex += (fromIndex < 0) ? len : 0;
            for (; fromIndex < len; ++fromIndex) {
                if (this[fromIndex] === searchElement) {
                    return fromIndex;
                }
            }
            return -1;
        },
        /**
         * Removes the specified object from the array.  If the object is not found nothing happens.
         * @param {Object} o The object to remove
         * @return {Array} this array
         */
        remove: function (o) {
            var index = this.indexOf(o);
            if (index !== -1) {
                this.splice(index, 1);
            }
            return this;
        }
    });

    Jxt.util = {};

    Jxt.util.Event = function (owner, name) {
        this.owner = owner;
        this.name = name;
        this.listeners = [];
    };
    Jxt.util.Event.prototype = {
        addListener: function (fn, scope) {
            this.listeners.push({
                fn: fn,
                scope: scope
            });
        },
        fire: function (args) {
            for (var i = 0; i < this.listeners.length; i++) {
                var l = this.listeners[i];
                l.fn.apply(l.scope || this.owner, args);
            }
        }
    };

    /**
     * @package Jxt.util
     * @class Jxt.util.Observable
     */
    Jxt.util.Observable = function () {
        this.events = {};
    };
    Jxt.util.Observable.prototype = {
        addEvent: function (eventName) {
            if (!this.events[eventName]) {
                this.events[eventName] = new Jxt.util.Event(this, eventName);
            }
            return this.events[eventName];
        },
        addListener: function (eventName, fn, scope) {
            this.addEvent(eventName).addListener(fn, scope);
        },
        fireEvent: function (eventName) {
            var args = Array.prototype.slice.call(arguments, 1);
            this.events[eventName] && this.events[eventName].fire(args);
        }
    };
    Jxt.util.Observable.prototype.constructor = Jxt.util.Observable;

    /**
     * MixedCollection
     */
    Jxt.util.MixedCollection = function () {
        this.length = 0;
        this.items = [];
        this.keys = [];
        this.map = {};
    };
    Jxt.util.MixedCollection.prototype = {
        add: function (key, item) {
            if (this.keys.indexOf(key) !== -1) {
                this.keys.push(key);
                this.items.push(item);
                this.map[key] = this.items[this.length];
                this.length++;
            }
            return this;
        },
        clear: function () {
            this.length = 0;
            this.items = [];
            this.keys = [];
            this.map = {};
        },
        contains: function (o) {
            return this.items.indexOf(o) !== -1;
        },
        containsKey: function (key) {
            return this.keys.indexOf(key) !== -1;
        },
        each: function (fn, scope) {
            for (var i = 0; i < this.items.length; i++) {
                if (fn.call(scope || this.items[i], this.items[i], i, this.items.length) === false) {
                    break;
                }
            }
            return this;
        },
        eachKey: function (fn, scope) {
            for (var i = 0; i < this.keys.length; i++) {
                fn.call(scope || window, this.keys[i], this.map[this.keys[i]], i, this.keys.length);
            }
            return this;
        },
        first: function () {
            return this.items[0];
        },
        /**
         * Returns the item associated with the passed key OR index.
         *
         * @param {String/Number} key The key or index of the item.
         * @return {Object} If the item is found, returns the item.  If the item was not found, returns undefined.
         */
        get: function (key) {
            if (typeof key === 'number') {
                return this.items[key];
            } else {
                return this.map[String(key)];
            }
        },
        /**
         * Returns the number of items in the collection.
         *
         * @return {Number}
         */
        getCount: function () {
            return this.length;
        }
    };


    Jxt.ComponentMgr = (function () {
        var id = {};
        var xtype = {};
        return {
            /**
             * Returns a component by id
             *
             * @param {String} id
             */
            get: function (id) {
                return id[String(id)];
            },
            /**
             * 组件寄存器，使用组件的id缓存组件，以便快速获得组件
             * Jxt Component所有子类都会自动缓存实例化的组件
             *
             * @param {Object} cmp
             */
            register: function (cmp) {
                if (cmp.id) {
                    id[cmp.id] = cmp;
                }
                return this;
            },
            /**
             * 将组件构造函数注册为xtype，以便xtype方式使用组件
             *
             * @param {String} xtype
             * @param {Function} constructor
             */
            registerType: function (xtype, constructor) {
                if (xtype && constructor) {
                    xtype[xtype] = constructor;
                }
                return this;
            }
        };
    })();
    Jxt.reg = Jxt.ComponentMgr.registerType;
    Jxt.get = Jxt.ComponentMgr.get;

    /**
     * @class Jxt.Component
     * @extends Jxt.util.Observable
     */
    Jxt.Component = Jxt.extend(Jxt.util.Observable, {
        isComponent: true,
        constructor: function (config) {
            Jxt.Component.superclass.constructor.call(this);
            this.config = config = config || {};
            this.id = config.id || Jxt.getId();
            Jxt.ComponentMgr.register(this);
            this.initComponent();
        },
        initComponent: function () {
            this.rendered = false;
        },
        getEl: function () {
            return this.el;
        },
        getId: function () {
            return this.id;
        }
    });
    Jxt.reg('component', Jxt.Component);

    /**
     * BoxComponent: a component manage width and height
     */
    Jxt.BoxComponent = Jxt.extend(Jxt.Component, {
        initComponent: function () {
            Jxt.BoxComponent.superclass.initComponent.call(this);
            this.el = jQuery('<div></div>');
        },
        setWidth: function (width) {
            this.el.width(width);
            return this;
        },
        setHeight: function (height) {
            this.el.height(height);
            return this;
        },
        render: function (target) {
            jQuery(target).append(this.el);
            return this;
        },
        remove: function () {
            this.el && this.el.remove();
            delete this;
        }
    });
    Jxt.reg('box', Jxt.BoxComponent);

    /**
     * base component with items
     */
    Jxt.Container = Jxt.extend(Jxt.BoxComponent, {
        initComponent: function () {
            this.items = this.config.items || [];
            this.el = jQuery('<div></div>');
            Jxt.Container.superclass.initComponent.call(this);
            var items = this._items = new Jxt.util.MixedCollection();
        },
        add: function (cmp) {
            this.items.push(cmp);
        },
        render: function (target) {
            Jxt.each(this.items, function (item) {
                item.render(this.el);
            }, this);

            jQuery(target).append(this.el);
            this.rendered = true;
            return this;
        }
    });
    Jxt.reg('container', Jxt.Container);

    window.Jxt = Jxt;
})(window);
